/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.pdx.cs.guavadata.query;

import edu.pdx.cs.guavadata.Channel;
import edu.pdx.cs.guavadata.Transform;
import edu.pdx.cs.guavadata.condition.Condition;
import edu.pdx.cs.guavadata.subclass.ConditionType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author VuTran
 */
public class Update extends Operator {

    // DML statement for updating rows in a table

    String table;
    List<String> condColumns;
    List<Condition> conditions;
    List<String> columns;
    Operator child;
    Map<String, Condition> idJoin;

    public Operator getChild() {
        return child;
    }

    public void setChild(Operator child) {
        this.child = child;
    }

    public List<String> getColumns() {
        return columns;
    }

    public void setColumns(List<String> columns) {
        this.columns = columns;
    }

    public List<String> getCondColumns() {
        return condColumns;
    }

    public void setCondColumns(List<String> condColumns) {
        this.condColumns = condColumns;
    }

    public List<Condition> getConditions() {
        return conditions;
    }

    public void setConditions(List<Condition> conditions) {
        this.conditions = conditions;
    }

    public String getTable() {
        return table;
    }

    public void setTable(String table) {
        this.table = table;
    }

    public Update(String table, Collection<String> columns, Operator query) {
        this.table = table;
        this.condColumns = new ArrayList<String>();
        this.conditions =  new ArrayList<Condition>();
        this.columns = new ArrayList<String>(columns);
        this.child = query;
        this.parent = null;
        this.idJoin = null;

        try {
            // Check all of the conditions are equality conditions
            for (Condition c : this.conditions) {
                if (c.getCondType() != ConditionType.EQ)
                    throw new Exception("Found a condition of type " + c.getCondType().toString() +
                            " in an update statement - only equality conditions are accepted.");
            }

            // Dummy check
            if (query.exposedColumns == null)
                throw new Exception("Query to produce rows does not have any exposed columns, and is of type " +
                        query.getClass().getName() + ".");
            if (this.columns.size() != query.exposedColumns.size())
                throw new Exception("Column counts between expected columns " + this.columns.size() +
                        " and query exposed columns " + query.exposedColumns.size() + " do not match.");
        } catch (Exception e) {
            System.err.println("ERROR: " + e.toString());
        }
    }

    public Update(String table, List<String> condColumns, List<Condition> conditions,
            List<String> columns, Operator query) {
        this.table = table;
        this.condColumns = new ArrayList<String>(condColumns);
        this.conditions = new ArrayList<Condition>(conditions);
        this.columns = new ArrayList<String>(columns);
        this.child = query;
        this.parent = null;
        this.idJoin = null;

        try {
            // Check all of the conditions are equality conditions
            for (Condition c : this.conditions) {
                if (c.getCondType() != ConditionType.EQ)
                    throw new Exception("Found a condition of type " + c.getCondType().toString() +
                            " in an update statement - only equality conditions are accepted.");
            }

            // Dummy check
            if (query.exposedColumns == null)
                throw new Exception("Query to produce rows does not have any exposed columns, and is of type " +
                        query.getClass().getName() + ".");
            if (this.columns.size() != query.exposedColumns.size())
                throw new Exception("Column counts between expected columns " + this.columns.size() +
                        " and query exposed columns " + query.exposedColumns.size() + " do not match.");
            if (this.condColumns.size() != this.conditions.size())
                throw new Exception("Column counts between expected condition columns " + this.condColumns.size() +
                        " and conditions " + this.conditions.size() + " do not match.");
        } catch (Exception e) {
            System.err.println("ERROR: " + e.toString());
        }
    }

    public void addCondition(String column, Condition condition) {
        try {
            if (condition.getCondType() != ConditionType.EQ) {
                throw new Exception("Found a condition of type " + condition.getCondType().toString() +
                        " in an update statement - only equality conditions are accepted.");
            }

            this.condColumns.add(column);
            this.conditions.add(condition);
        } catch (Exception e) {
            System.err.println("ERROR: " + e.toString());
        }
    }

    public boolean needJoin() {
        return (idJoin != null);
    }

    public String getJoinString() {
        if (idJoin == null || idJoin.size() == 0)
            return null;

        String build = "";
        for (String key : idJoin.keySet()) {
            build = build + this.table + "." + key + idJoin.get(key).toString() + " AND ";
        }
        return build.substring(0, build.length() - 5);
    }

    public void addJoin(String col, Condition cond) {
        if (idJoin == null)
            idJoin = new HashMap<String, Condition>();
        idJoin.put(col, cond);
    }

    public String getConditionString() {
        if (this.conditions == null || this.conditions.size() == 0)
            return null;

        String build = "";
        for (int i = 0; i < this.conditions.size(); i++) {
            build = build + (needJoin() ? this.table + "." : "") + condColumns.get(i) +
                    conditions.get(i).toString() + " AND ";
        }
        return build.substring(0, build.length() - 5);
    }

    @Override
    public boolean isQuery() {
        return false;
    }

    @Override
    public SqlText visit(Provider qv) {
        return qv.transform(this);
    }

    @Override
    public void visit(Channel c, String table) {
        c.mapAliases(this, table);
    }

    @Override
    public void visit(Transform t, Command c) {
        t.expand(this, c);
    }

    @Override
    public void notifyChange(Operator old, Operator newOp) {
        if (this.child == old) {
            this.child = newOp;
            newOp.parent = this;
        }
    }

    @Override
    public Operator clone() {
        Update uNew = new Update(table, condColumns, conditions, columns, child.clone());
        for (String s : idJoin.keySet())
            uNew.addJoin(s, this.idJoin.get(s));
        return uNew;
    }

}
